/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package kz.dasm.baing.db;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import javax.persistence.Column;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;

/**
 *
 * @author Kreedmart
 */
public class ResultSetMapper {

  static final private Logger logger = Logger.getLogger(
          ResultSetMapper.class.getName());


  static public <T> T mapToBean(ResultSet rs, Class<T> outputClass)
          throws SQLException {

    try {

      List<Field> flds = new ArrayList<Field>();
      getAllFields(outputClass, flds);              
              
      T bean = outputClass.newInstance();

      for (Field f : flds) {
        Column annotation = f.getAnnotation(Column.class);

        if (annotation == null) {
          continue;
        }

        String columnName = annotation.name();

        if (f.getType().isPrimitive()) {
          processPrimitives(bean, f, columnName, rs);
        } else {
          processObjects(bean, f, columnName, rs);
        }

      }

      return bean;

    } catch (IllegalAccessException ex) {
      throw new RuntimeException(ex);

    } catch (InstantiationException ex) {
      throw new RuntimeException(ex);

    } catch (InvocationTargetException ex) {
      throw new RuntimeException(ex);

    } catch (NoSuchMethodException ex) {
      throw new RuntimeException(ex);
    }

  }


  static public <T> List<T> mapToBeanList(ResultSet rs, Class<T> outputClass)
          throws SQLException {
    List<T> res = new ArrayList<T>();

    while (rs.next()) {
      res.add(mapToBean(rs, outputClass));
    }
    return res;
  }


  static private void processPrimitives(Object obj, Field f, String columnName, ResultSet rs)
          throws SQLException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    Class<?> fieldType = f.getType();

    if (fieldType == int.class) {
      BeanUtils.setProperty(obj, f.getName(), rs.getInt(columnName));

    } else if (fieldType == float.class) {
      BeanUtils.setProperty(obj, f.getName(), rs.getFloat(columnName));
      
    } else if (fieldType == long.class) {
      BeanUtils.setProperty(obj, f.getName(), rs.getLong(columnName));

    } else if (fieldType == boolean.class) {
      BeanUtils.setProperty(obj, f.getName(), rs.getBoolean(columnName));

    } else {
      throw new UnsupportedOperationException(
              "Field " + f.getName()
              + " has unsupported primitive type " + fieldType.getSimpleName());
    }

  }


  static private void processObjects(Object obj, Field f, String columnName, ResultSet rs)
          throws SQLException, IllegalArgumentException, IllegalAccessException,
          InvocationTargetException, NoSuchMethodException {

    Class<?> fieldType = f.getType();

    if (fieldType == Integer.class) {
      Integer value = rs.getInt(columnName);
      PropertyUtils.setProperty(obj, f.getName(), rs.wasNull() ? null : value);

    } else if (fieldType == Float.class) {
      Float value = rs.getFloat(columnName);
      PropertyUtils.setProperty(obj, f.getName(), rs.wasNull() ? null : value);
      
    } else if (fieldType == Long.class) {
      Long value = rs.getLong(columnName);
      PropertyUtils.setProperty(obj, f.getName(), rs.wasNull() ? null : value);

    } else if (fieldType == java.util.Date.class) {
      java.sql.Timestamp sqlTimestamp = rs.getTimestamp(columnName);
      java.util.Date date = null;

      if (sqlTimestamp != null) {
        date = new Date(sqlTimestamp.getTime());
      }

      PropertyUtils.setProperty(obj, f.getName(), date);

    } else {
      PropertyUtils.setProperty(obj, f.getName(), rs.getObject(columnName));
//      f.set(obj, rs.getObject(columnName));
    }

  }


  static private <T> void getAllFields(Class<T> cls, List<Field> fields) {
    Class<? super T> superClass = cls.getSuperclass();
    if (superClass != null) {
      getAllFields(superClass, fields);
    }

    Field[] flds = cls.getDeclaredFields();

    fields.addAll(Arrays.asList(flds));
  }
}
